package system

import (
	"log"
	"time"

	"github.com/asynkron/protoactor-go/actor"
)

// SystemSupervisor is the top-level supervisor for the entire system
type SystemSupervisor struct {
	// Children are the child actors managed by this supervisor
	children map[string]*actor.PID
	// Logger is the supervisor logger
	logger *log.Logger
}

// NewSystemSupervisor creates a new system supervisor
func NewSystemSupervisor() actor.Actor {
	return &SystemSupervisor{
		children: make(map[string]*actor.PID),
		logger:   log.New(log.Writer(), "[SystemSupervisor] ", log.LstdFlags),
	}
}

// Receive handles messages for the system supervisor
func (s *SystemSupervisor) Receive(context actor.Context) {
	switch msg := context.Message().(type) {
	case *actor.Started:
		s.handleStarted(context)
	case *actor.Stopping:
		s.handleStopping(context)
	case *actor.Restarting:
		s.handleRestarting(context)
	case *actor.Terminated:
		s.handleTerminated(context, msg)
	case *SpawnChildCommand:
		s.handleSpawnChild(context, msg)
	case *StopChildCommand:
		s.handleStopChild(context, msg)
	case *GetChildrenCommand:
		s.handleGetChildren(context)
	case *ErrorMessage:
		s.handleError(context, msg)
	default:
		s.logger.Printf("Received unknown message: %T", msg)
	}
}

// handleStarted handles the Started message
func (s *SystemSupervisor) handleStarted(context actor.Context) {
	s.logger.Println("System supervisor started")

	// Spawn essential system actors
	s.spawnClusterManager(context)
	s.spawnWorkflowSupervisor(context)
	s.spawnStateManager(context)
	s.spawnMetricsCollector(context)
}

// handleStopping handles the Stopping message
func (s *SystemSupervisor) handleStopping(context actor.Context) {
	s.logger.Println("System supervisor stopping")

	// Stop all children
	for name, pid := range s.children {
		s.logger.Printf("Stopping child: %s", name)
		context.Stop(pid)
	}
}

// handleRestarting handles the Restarting message
func (s *SystemSupervisor) handleRestarting(context actor.Context) {
	s.logger.Println("System supervisor restarting")
}

// handleTerminated handles the Terminated message
func (s *SystemSupervisor) handleTerminated(context actor.Context, msg *actor.Terminated) {
	// Find and remove the terminated child
	for name, pid := range s.children {
		if pid.String() == msg.Who.String() {
			s.logger.Printf("Child terminated: %s", name)
			delete(s.children, name)
			break
		}
	}
}

// handleSpawnChild handles the SpawnChildCommand message
func (s *SystemSupervisor) handleSpawnChild(context actor.Context, msg *SpawnChildCommand) {
	// Check if child already exists
	if _, exists := s.children[msg.Name]; exists {
		context.Respond(&SpawnChildResponse{
			Success: false,
			Error:   "Child with this name already exists",
		})
		return
	}

	// Spawn the child actor
	pid, err := context.SpawnNamed(msg.Props, msg.Name)
	if err != nil {
		context.Respond(&SpawnChildResponse{
			Success: false,
			Error:   err.Error(),
		})
		return
	}

	// Store the child
	s.children[msg.Name] = pid

	// Respond with success
	context.Respond(&SpawnChildResponse{
		Success: true,
		PID:     pid,
	})
}

// handleStopChild handles the StopChildCommand message
func (s *SystemSupervisor) handleStopChild(context actor.Context, msg *StopChildCommand) {
	// Check if child exists
	pid, exists := s.children[msg.Name]
	if !exists {
		context.Respond(&StopChildResponse{
			Success: false,
			Error:   "Child not found",
		})
		return
	}

	// Stop the child
	context.Stop(pid)

	// Remove from children map
	delete(s.children, msg.Name)

	// Respond with success
	context.Respond(&StopChildResponse{
		Success: true,
	})
}

// handleGetChildren handles the GetChildrenCommand message
func (s *SystemSupervisor) handleGetChildren(context actor.Context) {
	// Create a copy of the children map
	children := make(map[string]*actor.PID)
	for name, pid := range s.children {
		children[name] = pid
	}

	// Respond with the children
	context.Respond(&GetChildrenResponse{
		Children: children,
	})
}

// handleError handles the ErrorMessage message
func (s *SystemSupervisor) handleError(context actor.Context, msg *ErrorMessage) {
	s.logger.Printf("Error from %s: %s", msg.ActorID, msg.Error)

	// Handle based on severity
	switch msg.Severity {
	case "critical":
		// For critical errors, stop the actor
		if pid, exists := s.children[msg.ActorID]; exists {
			s.logger.Printf("Stopping actor %s due to critical error", msg.ActorID)
			context.Stop(pid)
		}
	case "warning":
		// For warnings, just log
		s.logger.Printf("Warning from actor %s: %s", msg.ActorID, msg.Error)
	default:
		// For other errors, restart the actor
		if pid, exists := s.children[msg.ActorID]; exists {
			s.logger.Printf("Restarting actor %s due to error", msg.ActorID)
			context.RequestWithCustomSender(pid, &RestartCommand{}, context.Self())
		}
	}
}

// spawnClusterManager spawns the cluster manager actor
func (s *SystemSupervisor) spawnClusterManager(context actor.Context) {
	props := actor.PropsFromProducer(func() actor.Actor {
		return &EmptyActor{}
	})

	pid, err := context.SpawnNamed(props, "cluster-manager")
	if err != nil {
		s.logger.Printf("Failed to spawn cluster manager: %v", err)
		return
	}

	s.children["cluster-manager"] = pid
	s.logger.Println("Spawned cluster manager")
}

// spawnWorkflowSupervisor spawns the workflow supervisor actor
func (s *SystemSupervisor) spawnWorkflowSupervisor(context actor.Context) {
	props := actor.PropsFromProducer(func() actor.Actor {
		return NewSimpleSupervisor()
	})

	pid, err := context.SpawnNamed(props, "workflow-supervisor")
	if err != nil {
		s.logger.Printf("Failed to spawn workflow supervisor: %v", err)
		return
	}

	s.children["workflow-supervisor"] = pid
	s.logger.Println("Spawned workflow supervisor")
}

// spawnStateManager spawns the state manager actor
func (s *SystemSupervisor) spawnStateManager(context actor.Context) {
	props := actor.PropsFromProducer(func() actor.Actor {
		return &EmptyActor{}
	})

	pid, err := context.SpawnNamed(props, "state-manager")
	if err != nil {
		s.logger.Printf("Failed to spawn state manager: %v", err)
		return
	}

	s.children["state-manager"] = pid
	s.logger.Println("Spawned state manager")
}

// spawnMetricsCollector spawns the metrics collector actor
func (s *SystemSupervisor) spawnMetricsCollector(context actor.Context) {
	props := actor.PropsFromProducer(func() actor.Actor {
		return &EmptyActor{}
	})

	pid, err := context.SpawnNamed(props, "metrics-collector")
	if err != nil {
		s.logger.Printf("Failed to spawn metrics collector: %v", err)
		return
	}

	s.children["metrics-collector"] = pid
	s.logger.Println("Spawned metrics collector")
}

// NewSystemSupervisorStrategy creates a new system supervisor strategy
func NewSystemSupervisorStrategy() actor.SupervisorStrategy {
	return actor.NewOneForOneStrategy(10, 30*time.Second, func(reason interface{}) actor.Directive {
		switch reason.(type) {
		case *CriticalError:
			return actor.StopDirective
		case *TemporaryError:
			return actor.RestartDirective
		default:
			return actor.EscalateDirective
		}
	})
}

// EmptyActor is a placeholder actor that does nothing
type EmptyActor struct{}

// Receive handles messages for the empty actor
func (e *EmptyActor) Receive(context actor.Context) {}
